﻿using JinYuan.Models;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;

namespace JinYuan.Helper
{
    public class EnergyMeterHelper
    {
        public const int BufferSize = 1024;//6kb

        private int _timeout = 2000;
        private Ping _ping = null;
        private IPEndPoint _endPoint = null;
        private Socket EnergyMeterSocket = null;
        private PingReply _pingReply = null;
        private string _lastError = "";

        public enum ModBusExceptionCode    //错误代码
        {
            IllegalFunction = 01,
            IllegalDataAddress,
            IllegalDataValue,
            SlaveDeviceFailure,
            Acknowledge,
            SlaveDeviceBusy,
            GatewayPathUnavailable,
            GatewayTargetDeviceFailed2Respond,
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        public EnergyMeterHelper()
        {
            // used to ping the PLC
            //
            this._ping = new Ping();

            // EndPoint parametres
            //
            this._endPoint = new IPEndPoint(0, 0);
        }

        /// <summary>
        /// set ip and port
        /// </summary>
        public void SetTCPParams(IPAddress ip, int port)
        {
            this._endPoint.Address = ip;
            this._endPoint.Port = port;
        }

        /// <summary>
        /// returns the connection status
        /// </summary>
        public bool Connected
        {
            get
            {
                try
                {
                    return (EnergyMeterSocket == null) ? false : EnergyMeterSocket.Connected;
                }
                catch (Exception ex)
                {
                    //PrintfLog.LogError(ex.Message);
                    return false;
                }
            }
        }

        /// <summary>
        /// close the socket
        /// </summary>
        /// <returns></returns>
        public void Close()
        {
            lock (this)
            {
                if (EnergyMeterSocket == null) return;
                if (Connected)
                {
                    EnergyMeterSocket.Disconnect(false);
                    EnergyMeterSocket.Close();
                }
                EnergyMeterSocket.Dispose();
                EnergyMeterSocket = null;
            }
        }

        public bool Connect()
        {
            if (this.Connected == true)
            {
                return true;
            }

            try
            {
                if (this.TCPConnect())
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                Close();
                return false;
            }
        }

        private bool TCPConnect()
        {
            try
            {
                if (EnergyMeterSocket != null)
                {
                    EnergyMeterSocket.Dispose();
                }
                EnergyMeterSocket = new Socket(_endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

                EnergyMeterSocket.SendTimeout = _timeout;
                EnergyMeterSocket.ReceiveTimeout = _timeout;
                EnergyMeterSocket.Connect(this._endPoint);
                return this.Connected;
            }
            catch (System.Exception ex)
            {
                TxtHelper.WriteTxt($@"D:\APILog\Logs\智能电表信息\{DateTime.Now.ToString("yyyy-MM-dd")}.txt", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 智能电表：网口连接失败");
                return false;
            }
        }

        public bool Ping()
        {
            try
            {
                if (this._endPoint.Address == null) return false;

                this._pingReply = this._ping.Send(this._endPoint.Address, this._timeout);

                return (this._pingReply.Status == IPStatus.Success) ? true : false;

            }
            catch (Exception)
            {
                //PrintfLog.LogError("ECFFU ping exception");
                return false;
            }
        }

        public bool SendAndGetRes(byte[] bytes, ref List<ElectricEnergy> listM)//, ref JetReturnResult jet
        {
            var arrayPool = ArrayPool<byte>.Shared;
            byte[] buffer = arrayPool.Rent(BufferSize);//new byte[1024];
            byte[] RecvData = new byte[88 * 4 + 9];//0x58
            int size = 88 * 4 + 9;//0x58

            if (!Ping())
            {
                return false;
            }
            try
            {
                // 发送
                Send(bytes, bytes.Length);
                // 接收
                Receive(ref RecvData, size);
                // 解析
                if ((RecvData[0] == Convert.ToByte(01 >> 8) && RecvData[1] == Convert.ToByte(01)))//pTask.id
                {
                    if (RecvData[7] == 0x04)//读线圈寄存器功能码
                    {
                        //数据赋值
                        Array.Copy(RecvData, 0, buffer, 0, RecvData.Length);
                        TxtHelper.WriteTxt($@"D:\APILog\Logs\智能电表信息\{DateTime.Now.ToString("yyyy-MM-dd")}.txt", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + $"电能表读取原始数据:{string.Join(" ", RecvData)}");

                        decimal PhaseVoltageA = Convert.ToDecimal(HextoFloat(buffer[9], buffer[10], buffer[11], buffer[12]));
                        decimal PhaseVoltageB = Convert.ToDecimal(HextoFloat(buffer[13], buffer[14], buffer[15], buffer[16]));
                        decimal PhaseVoltageC = Convert.ToDecimal(HextoFloat(buffer[17], buffer[18], buffer[19], buffer[20]));
                        decimal PhaseCurrentA = Convert.ToDecimal(HextoFloat(buffer[21], buffer[22], buffer[23], buffer[24]));
                        decimal PhaseCurrentB = Convert.ToDecimal(HextoFloat(buffer[25], buffer[26], buffer[27], buffer[28]));
                        decimal PhaseCurrentC = Convert.ToDecimal(HextoFloat(buffer[29], buffer[30], buffer[31], buffer[32]));
                        decimal PhasePowerA = Convert.ToDecimal(HextoFloat(buffer[33], buffer[34], buffer[35], buffer[36]));
                        decimal PhasePowerB = Convert.ToDecimal(HextoFloat(buffer[37], buffer[38], buffer[39], buffer[40]));
                        decimal PhasePowerC = Convert.ToDecimal(HextoFloat(buffer[41], buffer[42], buffer[43], buffer[44]));
                        decimal ActivePower = Convert.ToDecimal(HextoFloat(buffer[121], buffer[122], buffer[123], buffer[124]));
                        decimal PowerFactor = Convert.ToDecimal(HextoFloat(buffer[133], buffer[134], buffer[135], buffer[136]));
                        decimal pt = 1;
                        decimal ct = 150 / 5;
                        decimal AccumulatedElectricity = Convert.ToDecimal(HextoFloat(buffer[181], buffer[182], buffer[183], buffer[184]));

                        ElectricEnergy m = new ElectricEnergy();
                        m.PhaseVoltageA = PhaseVoltageA;
                        m.PhaseVoltageB = PhaseVoltageB;
                        m.PhaseVoltageC = PhaseVoltageC;
                        m.PhaseCurrentA = PhaseCurrentA * ct;
                        m.PhaseCurrentB = PhaseCurrentB * ct;
                        m.PhaseCurrentC = PhaseCurrentC * ct;
                        m.PhasePowerA = PhaseVoltageA * PhaseCurrentA * ct;
                        m.PhasePowerB = PhaseVoltageB * PhaseCurrentB * ct;
                        m.PhasePowerC = PhaseVoltageC * PhaseCurrentC * ct;
                        m.ActivePower = ActivePower * ct;
                        m.PowerFactor = PowerFactor * ct;
                        m.pt = pt;//Convert.ToDecimal(HextoFloat(buffer[0], buffer[0], buffer[0], buffer[0]));
                        m.ct = ct;//Convert.ToDecimal(HextoFloat(buffer[0], buffer[0], buffer[0], buffer[0]));
                        m.AccumulatedElectricity = AccumulatedElectricity;
                        listM.Add(m);
                    }
                    else
                    {
                        switch (RecvData[8])
                        {
                            case (byte)ModBusExceptionCode.IllegalDataAddress:
                                _lastError = string.Format("SendAndGetRes():ReadInputRegister [IllegalDataAddress]");
                                break;
                            case (byte)ModBusExceptionCode.IllegalDataValue:
                                _lastError = string.Format("SendAndGetRes():ReadInputRegister [IllegalDataValue]");
                                break;
                            case (byte)ModBusExceptionCode.IllegalFunction:
                                _lastError = string.Format("SendAndGetRes():ReadInputRegister [IllegalFunction]");
                                break;
                            default:
                                _lastError = string.Format("SendAndGetRes():ReadInputRegister [FunctionException]");
                                break;
                        }
                        TxtHelper.WriteTxt($@"D:\APILog\Logs\智能电表信息\{DateTime.Now.ToString("yyyy-MM-dd")}.txt", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + $"电能表读取数据异常,原因:{_lastError}");
                    }
                }
            }
            catch (Exception ex)
            {
                TxtHelper.WriteTxt($@"D:\APILog\Logs\智能电表信息\{DateTime.Now.ToString("yyyy-MM-dd")}.txt", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + $"电能表读取原始数据:{string.Join(" ", RecvData)}");
                TxtHelper.WriteTxt($@"D:\APILog\Logs\智能电表信息\{DateTime.Now.ToString("yyyy-MM-dd")}.txt", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + $"电能表读取数据异常,原因:{ex}");
                Close();
                return false;
            }
            finally
            {
                arrayPool.Return(buffer);
            }
            return true;
        }

        /// <summary>
        /// send a command to energy meter device
        /// </summary>
        /// <param name="command"></param>
        /// <param name="cmdLen"></param>
        /// <returns></returns>
        private int Send(Byte[] command, int cmdLen)
        {
            if (!Connected)
            {
                throw new Exception("EnergyMeter Socket is not connected.");
            }
            // sends the command
            //
            int bytesSent = EnergyMeterSocket.Send(command, cmdLen, SocketFlags.None);

            // it checks the number of bytes sent
            //
            if (bytesSent != cmdLen)
            {
                string msg = string.Format("EnergyMeter Sending error. (Expected bytes: {0}  Sent: {1})", cmdLen, bytesSent);
                throw new Exception(msg);
            }
            return bytesSent;
        }

        /// <summary>
        /// receives a response from the plc
        /// </summary>
        /// <param name="response"></param>
        /// <param name="respLen"></param>
        /// <returns></returns>
        private int Receive(ref Byte[] response, int respLen)
        {
            if (!this.Connected)
            {
                throw new Exception("EnergyMeter Socket is not connected.");
            }

            // receives the response, this is a synchronous method and can hang the process        
            int bytesRecv = EnergyMeterSocket.Receive(response, respLen, SocketFlags.None);

            // check the number of bytes received
            //
            //             if (bytesRecv != respLen)
            //             {
            //                 string msg = string.Format("Receiving error. (Expected: {0}  Received: {1})"
            //                                             , respLen, bytesRecv);
            //                 throw new Exception(msg);
            //             }
            return bytesRecv;
        }


        /// <summary>
        /// HexToFloat
        /// </summary>
        /// <param name="H1">高字高字节</param>
        /// <param name="H2">高字低字节</param>
        /// <param name="D1">低字高字节</param>
        /// <param name="D2">低字低字节</param>
        /// <returns>float</returns>
        public static float HextoFloat(byte H1, byte H2, byte D1, byte D2)
        {
            try
            {
                //byte-->short
                int s1, s2;
                s1 = Convert.ToInt32(H1 * 256) + Convert.ToInt32(H2);
                s2 = Convert.ToInt32(D1 * 256) + Convert.ToInt32(D2);

                //将输入数值short转化为无符号unsigned short
                int us1 = s1, us2 = s2;
                if (s1 < 0) us1 += 65536;
                if (s2 < 0) us2 += 65536;
                //sign: 符号位, exponent: 阶码, mantissa:尾数
                int sign, exponent;
                float mantissa;
                //计算符号位
                sign = us1 / 32768;
                //去掉符号位
                int emCode = us1 % 32768;
                //计算阶码
                exponent = emCode / 128;
                //计算尾数
                mantissa = (float)(emCode % 128 * 65536 + us2) / 8388608;
                //代入公式 fValue = (-1) ^ S x 2 ^ (E - 127) x (1 + M)
                return (float)Math.Pow(-1, sign) * (float)Math.Pow(2, exponent - 127) * (1 + mantissa);
            }
            catch (Exception ex)
            {
                return 0;
            }
        }
    }
}
